=begin
 *   Copyright (C) 2003 Guillaume Pierronnet <moumar@rubyforge.org>
 *  
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,MA  02111-1307  USA 
 *
 */
=end


require "c_mp3tag"

class Mp3InfoError < StandardError ; end

class Mp3InfoInternalError < StandardError ; end #:nodoc:

class Mp3Tag
  
  
  MPEG_VERSION = [ 2, nil, 2, 1]
  LAYER = [ nil, 3, 2, 1]
  BITRATE = [ 
    [ 
      [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448],
      [32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384],
      [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] ],
    [ 
      [32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
      [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
      [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
    ]
  ]
  SAMPLERATE = [
    [ 44100, 48000, 32000 ],
    [ 22050, 24000, 16000 ]
  ]
  CHANNEL_MODE = [ "Stereo", "JStereo", "Dual Channel", "Single Channel"]

  # mpeg version = 1 or 2
  attr_reader(:mpeg_version)

  # layer = 1, 2, or 3
  attr_reader(:layer)
  
  # bitrate in kbps
  attr_reader(:bitrate)

  # samplerate in Hz
  attr_reader(:samplerate)
  
  # channel mode = "Stereo", "JStereo", "Dual Channel" or "Single Channel"
  attr_reader(:channel_mode)

  # variable bitrate = true or false
  attr_reader(:vbr)

  # length in seconds
  attr_reader(:length)

  # error protection = true or false
  attr_reader(:error_protection)
  

  def Mp3Tag.id3v1_genre(nop)
    return @@id3v1_genre[nop]
  end
    
  
  @@id3v1_genre=[
    "Blues",             #0
    "Classic Rock",      #1
    "Country",           #2
    "Dance",             #3
    "Disco",             #4
    "Funk",              #5
    "Grunge",            #6
    "Hip-Hop",           #7
    "Jazz",              #8
    "Metal",             #9
    "New Age",           #10
    "Oldies",            #11
    "Other",             #12
    "Pop",               #13
    "R&B",               #14
    "Rap",               #15
    "Reggae",            #16
    "Rock",              #17
    "Techno",            #18
    "Industrial",        #19
    "Alternative",       #20
    "Ska",               #21
    "Death Metal",       #22
    "Pranks",            #23
    "Soundtrack",        #24
    "Euro-Techno",       #25
    "Ambient",           #26
    "Trip-Hop",          #27
    "Vocal",             #28
    "Jazz+Funk",         #29
    "Fusion",            #30
    "Trance",            #31
    "Classical",         #32
    "Instrumental",      #33
    "Acid",              #34
    "House",             #35
    "Game",              #36
    "Sound Clip",        #37
    "Gospel",            #38
    "Noise",             #39
    "AlternRock",        #40
    "Bass",              #41
    "Soul",              #42
    "Punk",              #43
    "Space",             #44
    "Meditative",        #45
    "Instrumental Pop",  #46
    "Instrumental Rock", #47
    "Ethnic",            #48
    "Gothic",            #49
    "Darkwave",          #50
    "Techno-Industrial", #51
    "Electronic",        #52
    "Pop-Folk",          #53
    "Eurodance",         #54
    "Dream",             #55
    "Southern Rock",     #56
    "Comedy",            #57
    "Cult",              #58
    "Gangsta",           #59
    "Top 40",            #60
    "Christian Rap",     #61
    "Pop/Funk",          #62
    "Jungle",            #63
    "Native American",   #64
    "Cabaret",           #65
    "New Wave",          #66
    "Psychadelic",       #67
    "Rave",              #68
    "Showtunes",         #69
    "Trailer",           #70
    "Lo-Fi",             #71
    "Tribal",            #72
    "Acid Punk",         #73
    "Acid Jazz",         #74
    "Polka",             #75
    "Retro",             #76
    "Musical",           #77
    "Rock & Roll",       #78
    "Hard Rock",         #79
    # following are winamp extentions
    "Folk",                  #80
    "Folk-Rock",             #81
    "National Folk",         #82
    "Swing",                 #83
    "Fast Fusion",           #84
    "Bebob",                 #85
    "Latin",                 #86
    "Revival",               #87
    "Celtic",                #88
    "Bluegrass",             #89
    "Avantgarde",            #90
    "Gothic Rock",           #91
    "Progressive Rock",      #92
    "Psychedelic Rock",      #93
    "Symphonic Rock",        #94
    "Slow Rock",             #95
    "Big Band",              #96
    "Chorus",                #97
    "Easy Listening",        #98
    "Acoustic",              #99
    "Humour",                #100
    "Speech",                #101
    "Chanson",               #102
    "Opera",                 #103
    "Chamber Music",         #104
    "Sonata",                #105
    "Symphony",              #106
    "Booty Bass",            #107
    "Primus",                #108
    "Porn Groove",           #109
    "Satire",                #110
    "Slow Jam",              #111
    "Club",                  #112
    "Tango",                 #113
    "Samba",                 #114
    "Folklore",              #115
    "Ballad",                #116
    "Power Ballad",          #117
    "Rhythmic Soul",         #118
    "Freestyle",             #119
    "Duet",                  #120
    "Punk Rock",             #121
    "Drum Solo",             #122
    "A capella",             #123
    "Euro-House",            #124
    "Dance Hall",            #125
    "Goa",                   #126
    "Drum & Bass",           #127
    "Club-House",            #128
    "Hardcore",              #129
    "Terror",                #130
    "Indie",                 #131
    "Britpop",               #132
    "Negerpunk",             #133
    "Polsk Punk",            #134
    "Beat",                  #135
    "Christian Gangsta Rap", #136
    "Heavy Metal",           #137
    "Black Metal",           #138
    "Crossover",             #139
    "Contemporary Christian",#140
    "Christian Rock ",       #141
    "Merengue",              #142
    "Salsa",                 #143
    "Trash Metal",           #144
    "Anime",                 #145
    "JPop",                  #146
    "Synthpop"               #147
  ]

  
  
  

  def initialize(filename)
    raise Mp3InfoError, "empty file" unless File.stat(filename).size? #FIXME
    @vbr = false
    @file = File.open(filename)
    begin
      a = find_frame_sync
      b, c = @file.read(4).unpack("C2")
      mpeg_version = (a >> 3) & 3
      @mpeg_version = MPEG_VERSION[mpeg_version]
      raise Mp3InfoInternalError if @mpeg_version.nil?
      layer = (a >> 1) & 3
      @layer = LAYER[layer]
      raise Mp3InfoInternalError if @layer.nil?
      @error_protection = a & 1 == 1 ? true : false
      bitrate = b >> 4
      @bitrate = BITRATE[@mpeg_version-1][@layer-1][bitrate-1] or raise Mp3InfoInternalError
      samplerate = (b >> 2) & 3
      @samplerate = SAMPLERATE[@mpeg_version-1][samplerate] or raise Mp3InfoInternalError
  #    @padding_bit = (b >> 1) & 1
      channel_mode = c >> 6
      @channel_mode = CHANNEL_MODE[channel_mode]

      seek = 
      if mpeg_version == 3			# mpeg version 1
        if channel_mode == 3 
	  15 					# Single Channel
        else
	  30
	end
      else					# mpeg version 2 or 2.5
        if channel_mode == 3 			# Single Channel
	  7	
        else
	  15
	end
      end

      @file.seek(seek, File::SEEK_CUR)

      if @file.read(4) == "Xing"			# This is a VBR file
        @vbr = true
        @file.seek(4, File::SEEK_CUR)			#we have the frames number after
	#if @file.read(4).unpack("N")[0] == 0xF
	#end
	@frames = @file.read(4).unpack("N")[0]
	medframe = @file.stat.size / @frames.to_f
	@bitrate = ( (medframe * @samplerate) /  ( 1000 * ( layer==3 ? 12 : 144) ) ).to_i
      end

      @length = (( (8 * @file.stat.size) / 1000) / @bitrate).to_i
      
      
      
    rescue Mp3InfoInternalError
      retry
    ensure
      @file.close
    end
  end
 
private
  def find_frame_sync
    last_byte = 0
    while b = @file.read(1)
      byte = b.unpack("C")[0]
      return byte if byte >> 5 == 7 and last_byte == 0xFF
      raise Mp3InfoError if @file.pos > 200000 or @file.eof?
      last_byte = byte
    end
  end
 
end
